﻿Imports System.Threading
Imports System.Globalization

Class DefaultContentXml(Of T As {New, IContent})
    Implements IContentXml


    Private _content_type As ContentType
    Private _write_properties As IEnumerable(Of ContentProperty)
    Private _reference_properties As IEnumerable(Of ContentReference)
    Private _property_instancer As New Dictionary(Of MysteryPropertyAttribute, IActivator)

    Private ref_text As ReferenceText

    Private Shared basic_filter_type As Type = GetType(ContentPropertyFilter(Of ))

    Private _filter_property_instancer As New Dictionary(Of MysteryPropertyAttribute, IActivator)

    Sub New()
        _content_type = getMystery().AssemblyRegister.getMysteryClassAttribute(Of ContentType, T)()

        Dim all As IEnumerable(Of ContentProperty) = getMystery().AssemblyRegister.getMysteryPropertyAttributes(Of ContentProperty, T)()
        _write_properties = New HashSet(Of ContentProperty)((From x As ContentProperty In all Select x Where x.retrive IsNot Nothing))
        Dim references As IEnumerable(Of ContentReference) = getMystery.AssemblyRegister.getMysteryPropertyAttributes(Of ContentReference, T)()
        _reference_properties = New HashSet(Of ContentReference)((From x As ContentReference In references Select x Where x.retrive IsNot Nothing))


        Dim fa As FastActivator = getGlobalObject(Of FastActivator)()

        For Each wp As ContentProperty In _write_properties
            'fedding _property_instancer
            Dim pt As Type = wp.used_in.PropertyType.GetGenericArguments.FirstOrDefault
            _property_instancer(wp) = fa.getActivator(wp.used_in.PropertyType)
            _filter_property_instancer(wp) = fa.getActivator(basic_filter_type.MakeGenericType(pt))
        Next

        ref_text = GetType(T).getFirstAttribute(Of ReferenceText)()

        For Each wp As ContentReference In _reference_properties
            'fedding _property_instancer
            Dim target_type As Type = wp.used_in.PropertyType.GetGenericArguments.FirstOrDefault
            Dim filter_type As Type = Nothing
            Select Case wp.used_in.PropertyType.GetGenericTypeDefinition
                Case GetType(SingleContentReference(Of ))
                    filter_type = GetType(ContentPropertySingleReferenceFilter(Of )).MakeGenericType(target_type)
                Case GetType(MultiContentReference(Of ))
                    filter_type = GetType(ContentPropertyMultiReferenceFilter(Of )).MakeGenericType(target_type)
            End Select

            _filter_property_instancer(wp) = fa.getActivator(filter_type)
            _property_instancer(wp) = fa.getActivator(wp.used_in.PropertyType)
        Next

    End Sub

    Public Function generateHasFilter(item As IContent) As Xml.XmlDocument Implements IContentXml.generateAsFilter
        Dim doc As New Xml.XmlDocument
        Dim root As Xml.XmlElement = doc.CreateElement(_content_type.name)
        doc.AppendChild(root)
        Dim guid_attr As Xml.XmlAttribute = doc.CreateAttribute("guid")
        guid_attr.Value = item.guid.ToString
        root.Attributes.Append(guid_attr)

        For Each wp As ContentProperty In _write_properties
            If wp.isReadOnly Then Continue For
            Dim pv As ContentPropertyValue = wp.retrive(item)
            If pv Is Nothing Then Continue For
            If Not TypeOf (pv) Is IContentPropertyFilter Then Continue For
            Dim pf As IContentPropertyFilter = pv
            Dim values As New List(Of Object)(pf.getValues())

            Dim ele As Xml.XmlElement = doc.CreateElement(wp.name)
            root.AppendChild(ele)

            For Each x As Object In values
                Dim v_ele As Xml.XmlElement = doc.CreateElement("item")
                ele.AppendChild(v_ele)
                Dim property_attr As Xml.XmlAttribute = doc.CreateAttribute("value")
                property_attr.Value = If(x Is Nothing, "", x.ToString)
                v_ele.Attributes.Append(property_attr)
            Next

        Next


        'references

        For Each rp As ContentReference In _reference_properties
            If rp.isReadOnly Then Continue For
            Dim pv As BaseContentReference = rp.retrive()(item)

            If pv Is Nothing Then Continue For
            If Not TypeOf (pv) Is IContentPropertyFilter Then Continue For
            Dim pf As IContentPropertyFilter = pv
            Dim values As New List(Of IContent)
            For Each o As Object In pf.getValues
                values.Add(o)
            Next

            Dim ele As Xml.XmlElement = doc.CreateElement(rp.name)
            root.AppendChild(ele)

            For Each c As IContent In values
                Dim v_ele As Xml.XmlElement = doc.CreateElement("item")
                ele.AppendChild(v_ele)
                Dim property_attr As Xml.XmlAttribute = doc.CreateAttribute("value")
                property_attr.Value = If(c Is Nothing, "", c.guid.ToString)
                v_ele.Attributes.Append(property_attr)
            Next

        Next


        Return doc

    End Function

    Public Function generate(item As IContent) As Xml.XmlDocument Implements IContentXml.generate
        Dim current As CultureInfo = Thread.CurrentThread.CurrentCulture

        Try
            Thread.CurrentThread.CurrentCulture = New CultureInfo("fr-FR")

            Dim doc As New Xml.XmlDocument
            Dim root As Xml.XmlElement = doc.CreateElement(_content_type.name)
            doc.AppendChild(root)
            Dim guid_attr As Xml.XmlAttribute = doc.CreateAttribute("guid")
            guid_attr.Value = item.guid.ToString
            root.Attributes.Append(guid_attr)

            For Each wp As ContentProperty In _write_properties
                Dim pv As ContentPropertyValue = wp.retrive(item)
                Dim value As Object = If(pv Is Nothing, "", pv.getValue())
                If value Is Nothing Then value = ""

                'I hate dates , when the hell they fix it, date.min is out of range in sql
                If TypeOf (value) Is Date Then
                    If value = Date.MinValue Then
                        value = ""
                    Else 'plus into string it goes depending the local setting, better to fix it
                        value = DirectCast(value, Date).ToString("yyyy-MM-dd HH:mm:ss")
                    End If
                Else
                    value = value.ToString
                End If

                Dim property_attr As Xml.XmlAttribute = doc.CreateAttribute(wp.name)
                property_attr.Value = value
                root.Attributes.Append(property_attr)
            Next

            If ref_text IsNot Nothing Then
                Dim value As String = ref_text.getReferenceText(item)
                Dim property_attr As Xml.XmlAttribute = doc.CreateAttribute(GetType(ReferenceText).Name)
                property_attr.Value = value
                root.Attributes.Append(property_attr)
            End If


            'references

            For Each rp As ContentReference In _reference_properties

                Dim pv As BaseContentReference = rp.retrive()(item)

                Dim referefece_ele As Xml.XmlElement = doc.CreateElement(rp.name)
                root.AppendChild(referefece_ele)
                Dim strings As New HashSet(Of String)
                For Each Guid As Guid In pv.getAsGuidEnum()
                    strings.Add(Guid.ToString)
                Next

                referefece_ele.InnerText = String.Join(",", strings.ToArray)

            Next

            Return doc
        Finally
            Thread.CurrentThread.CurrentCulture = current
        End Try

    End Function


    Public Function read(doc As Xml.XmlDocument) As IContent Implements IContentXml.read
        If doc Is Nothing Then Return Nothing

        Dim root As Xml.XmlElement = doc.FirstChild
        If root.Name <> _content_type.name Then Return Nothing 'wrong xml

        Dim current As CultureInfo = Thread.CurrentThread.CurrentCulture

        Try
            Thread.CurrentThread.CurrentCulture = New CultureInfo("fr-FR")
            Dim result As New T
            result.guid = Guid.Parse(root.Attributes("guid").Value)
            For Each wp As ContentProperty In _write_properties
                If wp.isReadOnly Then Continue For
                Dim attr As Xml.XmlAttribute = root.Attributes(wp.name)
                If attr Is Nothing Then Continue For
                Dim value_in_xml As String = attr.Value
                If String.IsNullOrEmpty(value_in_xml) Then value_in_xml = Nothing
                Dim pv As ContentPropertyValue = _property_instancer(wp).createInstance
                pv.setValue(value_in_xml)
                wp.save()(result, pv)
            Next

            For Each rp As ContentReference In _reference_properties
                If rp.isReadOnly Then Continue For
                Dim list As Xml.XmlNodeList = root.GetElementsByTagName(rp.name)
                If list Is Nothing Then Continue For
                If list.Count = 0 Then Continue For
                Dim ele As Xml.XmlElement = list(0)
                If ele Is Nothing Then Continue For
                Dim pv As BaseContentReference = rp.retrive()(result)
                Dim guids As IEnumerable(Of Guid) = (From x As String In ele.InnerText.Split(","c) Where Not String.IsNullOrEmpty(x.Trim()) Select Guid.Parse(x))
                pv.setWithGuids(guids)
            Next

            Return result
        Finally
            Thread.CurrentThread.CurrentCulture = current
        End Try

    End Function


    Public Function readAsFilter(doc As System.Xml.XmlDocument) As IContent Implements IContentXml.readAsFilter
        If doc Is Nothing Then Return Nothing

        Dim root As Xml.XmlElement = doc.FirstChild
        If root.Name <> _content_type.name Then Return Nothing 'wrong xml
        Dim result As New T

        Dim the_dict As New Dictionary(Of String, ICollection(Of String))

        For Each ele As Xml.XmlElement In root.ChildNodes
            Dim values As ICollection(Of String) = New LinkedList(Of String)
            For Each vele As Xml.XmlElement In ele.ChildNodes
                Dim attr As Xml.XmlAttribute = vele.Attributes("value")
                Dim v As String = If(attr Is Nothing, Nothing, attr.Value)
                values.Add(v)
            Next
            the_dict(ele.Name) = values



        Next

        For Each wp As ContentProperty In _write_properties
            If Not the_dict.ContainsKey(wp.name) Then Continue For
            Dim values As ICollection(Of String) = the_dict(wp.name)

            Dim pv As IContentPropertyFilter = _filter_property_instancer(wp).createInstance()
            pv.setValues(values)
            wp.save()(result, pv)
        Next

        For Each rp As ContentReference In _reference_properties
            If Not the_dict.ContainsKey(rp.name) Then Continue For
            Dim values As ICollection(Of String) = the_dict(rp.name)

            Dim pv As IContentPropertyFilter = _filter_property_instancer(rp).createInstance()
            pv.setValues(values)
            rp.save()(result, pv)
        Next

        Return result
    End Function
End Class
